/*
 * Copyright (c) 2025 chicken8225
 * All rights reserved.
 *
 * This file/program is protected by copyright law and international treaties.
 * Unauthorized reproduction or distribution of this file/program, or any portion
 * of it, may result in severe civil and criminal penalties.
 *
 * Date: 2025-07-21
 */
#include "infInt.h"

infInt::infInt() : num("0") {}
infInt::infInt(const infInt& other) : num(other.num) {}
infInt::infInt(infInt&& other) noexcept : num(std::move(other.num)) {}
infInt::infInt(const std::string& s) : num(s) { delZero(); }
infInt::infInt(int x) : num(std::to_string(x)) { delZero(); }

infInt& infInt::operator=(const infInt& other) {
    if (this != &other) {
        num = other.num;
        delZero();
    }
    return *this;
}

infInt& infInt::operator=(infInt&& other) noexcept {
    if (this != &other) num = std::move(other.num);
    return *this;
}

infInt& infInt::operator=(const std::string& s) {
    num = s;
    delZero();
    return *this;
}

infInt& infInt::operator=(const int& x) {
    num = std::to_string(x);
    delZero();
    return *this;
}

std::string infInt::copy() const {
    return num;
}

bool infInt::operator<(const infInt& other) const {
    if (num.size() != other.num.size()) return num.size() < other.num.size();
    return num < other.num;
}

bool infInt::operator<(const std::string& s) const {
    return *this < infInt(s);
}

bool infInt::operator<(const int& n) const {
    return *this < infInt(n);
}

bool infInt::operator==(const infInt& other) const {
    return num == other.num;
}

bool infInt::operator==(const std::string& s) const {
    return num == s;
}

bool infInt::operator==(const int& n) const {
    return *this == infInt(n);
}

bool infInt::operator>(const infInt& other) const {
    if (num.size() != other.num.size()) return num.size() > other.num.size();
    return num > other.num;
}

bool infInt::operator>(const std::string& s) const {
    return *this > infInt(s);
}

bool infInt::operator>(const int& n) const {
    return *this > infInt(n);
}

bool infInt::operator<=(const infInt& other) const {
    return *this < other || *this == other;
}

bool infInt::operator<=(const std::string& s) const {
    return *this <= infInt(s);
}

bool infInt::operator<=(const int& n) const {
    return *this <= infInt(n);
}

bool infInt::operator!=(const infInt& other) const {
    return !(*this == other);
}

bool infInt::operator!=(const std::string& s) const {
    return *this != infInt(s);
}

bool infInt::operator!=(const int& n) const {
    return *this != infInt(n);
}

bool infInt::operator>=(const infInt& other) const {
    return *this > other || *this == other;
}

bool infInt::operator>=(const std::string& s) const {
    return *this >= infInt(s);
}

bool infInt::operator>=(const int& n) const {
    return *this >= infInt(n);
}

infInt infInt::operator+(const infInt& other) const {
    std::string a = num, b = other.num;
    std::reverse(a.begin(), a.end());
    std::reverse(b.begin(), b.end());
    if (a.size() < b.size()) std::swap(a, b);
    int len1 = a.size(), len2 = b.size(), carry = 0;
    std::string ans = "";
    for(int i = 0; i < len1; i++) {
        int res = a[i] - '0' + carry;
        if (len2 > i) res += b[i] - '0';
        carry = res / 10;
        res %= 10;
        ans += res + '0';
    }
    if (carry > 0) ans += carry + '0';
    std::reverse(ans.begin(), ans.end());
    return infInt(ans);
}

infInt infInt::operator-(const infInt& other) const {
    std::string a = num, b = other.num;
    if (a < b) {
        throw std::domain_error("This type does not support negative numbers.");
    }
    std::reverse(a.begin(), a.end());
    std::reverse(b.begin(), b.end());
    int len1 = a.size(), len2 = b.size(), carry = 0;
    std::string tmp = "";
    for(int i = 0; i < len1; i++) {
        int res = a[i] - '0' - carry;
        if (len2 > i) res -= b[i] - '0';
        carry = res < 0;
        res = (res + 10) % 10;
        tmp += res + '0';
    }
    std::reverse(tmp.begin(), tmp.end());
    return infInt(tmp);
}

infInt infInt::operator*(const infInt& other) const {
    if (isZero() || other.isZero()) return infInt("0");
    if (*this == infInt("1")) return other;
    if (other == infInt("1")) return *this;
    if (num.size() <= KARATSUBA_THRESHOLD && other.num.size() <= KARATSUBA_THRESHOLD) {
        int a = stoi(num), b = stoi(other.num);
        return infInt(a * b);
    }
    size_t m = std::max(num.size(), other.num.size()) / 2;
    infInt high1(num.substr(0, num.size() - m));
    infInt low1(num.substr(num.size() - m));
    infInt high2(other.num.substr(0, other.num.size() - m));
    infInt low2(other.num.substr(other.num.size() - m));
    infInt z0 = low1 * low2;
    infInt z1 = (low1 + high1) * (low2 + high2);
    infInt z2 = high1 * high2;
    infInt part1 = z2;
    for (size_t i = 0; i < 2 * m; i++) part1.num += "0";
    infInt part2 = z1 - z2 - z0;
    for (size_t i = 0; i < m; i++) part2.num += "0";
    return part1 + part2 + z0;
}

infInt infInt::operator/(const infInt& other) const {
    if (other.isZero()) throw std::domain_error("The divisor cannot be zero.");
    if (*this < other) return infInt(0);
    if (*this == other) return infInt(1);
    infInt a = *this, c = 0, d = 0;
    for(auto ch : num) {
        d = d * infInt(10) + infInt(ch - '0');
        int digit = 0;
        while (d >= other) {
            d -= other;
            digit++;
        }
        c.num += std::to_string(digit);
    }
    c.delZero();
    return c;
}

infInt infInt::operator%(infInt other) const {
    if (other.isZero()) throw std::domain_error("The divisor cannot be zero.");
    if (other.num == "1") return infInt("0");
    if (*this < other) return *this;
    return *this - *this / other * other;
}

infInt infInt::operator^(const infInt& other) const {
    return this->pow(other);
}

infInt& infInt::operator+=(const infInt& other) {
    *this = *this + other;
    return *this;
}

infInt& infInt::operator-=(const infInt& other) {
    *this = *this - other;
    return *this;
}

infInt& infInt::operator*=(const infInt& other) {
    *this = *this * other;
    return *this;
}

infInt& infInt::operator/=(const infInt& other) {
    *this = *this / other;
    return *this;
}

infInt& infInt::operator%=(const infInt& other) {
    *this = *this % other;
    return *this;
}

infInt& infInt::operator^=(const infInt& other) {
    *this = this->pow(other);
    return *this;
}

infInt& infInt::operator++() {
    *this = *this + infInt("1");
    return *this;
}

infInt infInt::operator++(int) {
    infInt old = *this;
    ++(*this);
    return old;
}

infInt& infInt::operator--() {
    *this = *this - infInt("1");
    return *this;
}

infInt infInt::operator--(int) {
    infInt old = *this;
    --(*this);
    return old;
}

infInt infInt::pow(const infInt& other) const {
    if (other.isZero()) return infInt(1);
    infInt ans = 1, base = *this, exp = other;
    while (exp > 0) {
        if (exp % 2 == 1) ans *= base;
        base *= base;
        exp /= 2;
    }
    return ans;
}